/**************************************************************************//**
* @file     main.c
* @brief    Access SPI Authentication flash through QSPI interface
*
* SPDX-License-Identifier: Apache-2.0
* @copyright (C) 2018 Nuvoton Technology Corp. All rights reserved.
******************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include "nuc980.h"
#include "sys.h"
#include "qspi.h"
#include "gpio.h"

#include "rpmc.h"
#include "sha256.h"

#define DEF_KEY_INDEX 1

/*-----------------------------------------------------------------------------*/
void UART_Init()
{
    /* enable UART0 clock */
    outpw(REG_CLK_PCLKEN0, inpw(REG_CLK_PCLKEN0) | 0x10000);

    /* GPF11, GPF12 */
    outpw(REG_SYS_GPF_MFPH, (inpw(REG_SYS_GPF_MFPH) & 0xfff00fff) | 0x11000);  // UART0 multi-function

    /* UART0 line configuration for (115200,n,8,1) */
    outpw(REG_UART0_LCR, inpw(REG_UART0_LCR) | 0x07);
    outpw(REG_UART0_BAUD, 0x30000066); /* 12MHz reference clock input, 115200 */
}

void D2D3_SwitchToNormalMode(void)
{
    outpw(REG_SYS_GPD_MFPL, (inpw(REG_SYS_GPD_MFPL) & ~0xFF000000));
    GPIO_SetMode(PD, BIT6, GPIO_MODE_OUTPUT);
    GPIO_SetMode(PD, BIT7, GPIO_MODE_OUTPUT);
    PD6 = 1;
    PD7 = 1;
}

int32_t main(void)
{
    uint8_t  u8JID[4];
    uint8_t  u8UID[8];
    uint8_t  u8RootKey[32] = {0};
    uint8_t  u8HMACKey[32] = {0};
    uint8_t  u8InputTag[12] = {0};

    uint8_t  u8HMACMsgData[4];
    uint32_t u32RPMCStatus;
    uint32_t u32RPMCCounter;
    int count = 0;

    sysDisableCache();
    sysFlushCache(I_D_CACHE);
    sysEnableCache(CACHE_WRITE_BACK);

    UART_Init();

    /* Enable GPIO clock */
    outpw(REG_CLK_HCLKEN, inpw(REG_CLK_HCLKEN) | 0x800);

    /* Enable QSPI0 clock */
    outpw(REG_CLK_PCLKEN1, inpw(REG_CLK_PCLKEN1) | 0x10);

    /* Configure multi function pins to QSPI0 */
    outpw(REG_SYS_GPD_MFPL, (inpw(REG_SYS_GPD_MFPL) & ~0xFFFFFF00) | 0x11111100);

    D2D3_SwitchToNormalMode();

    /* Configure QSPI_FLASH_PORT as a master, MSB first, 8-bit transaction, SPI Mode-0 timing, clock is 20MHz */
    QSPI_Open(QSPI_FLASH_PORT, QSPI_MASTER, QSPI_MODE_0, 8, 20000000);

    /* Disable auto SS function, control SS signal manually. */
    QSPI_DisableAutoSS(QSPI_FLASH_PORT);

    /* Initialize SHA in CRYPTO. */
    CRYPTO_SHA_Initialize();

    printf("\n+-----------------------------------------------------------------------+\n");
    printf("|          NUC980 QSPI Sample with SPI Authentication Flash(W74M)        |\n");
    printf("+------------------------------------------------------------------------+\n");

    RPMC_ReadJEDECID(&u8JID[0]);
    printf("QSPI flash JEDEC ID: [%02X%02X%02X]\n", u8JID[0], u8JID[1], u8JID[2]);

    RPMC_ReadUID(&u8UID[0]);
    printf("QSPI flash UID [%02X%02X%02X%02X%02X%02X%02X%02X]\n", u8UID[0], u8UID[1], u8UID[2], u8UID[3], u8UID[4], u8UID[5], u8UID[6], u8UID[7]);

    /* First stage: Initial Rootkey */
    /* Calculate ROOTKey with UID & ROOTKeyTag by SHA256 */
    RPMC_CreateRootKey(&u8UID[0], sizeof(u8UID), &u8RootKey[0]);

    /* Initial Rootkey, use first rootkey/counter pair */
    u32RPMCStatus = RPMC_WriteRootKey(DEF_KEY_INDEX, &u8RootKey[0]);
    if (u32RPMCStatus == AF_REG_STATUS_SUCCESS)
    {
        printf("RPMC_WriteRootKey success - 0x%02X!!\n", u32RPMCStatus);
        /* Root Key Established */
        /* Monotonic Counter Initialized */
    }
    else
    {
        printf("RPMC_WriteRootKey failed - 0x%02X!!\n", u32RPMCStatus);
        /* OTP Rootkey exist? If yes, non-stop. */
        if (u32RPMCStatus != AF_REG_STATUS_BIT1)
            while (1);
    }

    /* Second stage, update HMACKey after ever power on. without update HMACkey, Gneiss would not function*/
    /* Get random data for HMAC message, it can also be serial number, RTC information and so on. */
    u8HMACMsgData[0] = rand() % 0x100;
    u8HMACMsgData[1] = rand() % 0x100;
    u8HMACMsgData[2] = rand() % 0x100;
    u8HMACMsgData[3] = rand() % 0x100;

    /* Update HMAC key and get new HMACKey. HMACKey is generated by SW using Rootkey and HMACMessage. */
    /* RPMC would also generate the same HMACKey by HW */
    u32RPMCStatus = RPMC_UpdateHMACKey(DEF_KEY_INDEX, &u8RootKey[0], &u8HMACMsgData[0], &u8HMACKey[0]);
    if (u32RPMCStatus == AF_REG_STATUS_SUCCESS)
    {
        printf("RPMC_UpdateHMACKey success - 0x%02X!!\n", u32RPMCStatus);
    }
    else
    {
        printf("RPMC_UpdateHMACKey failed - 0x%02X!!\n", u32RPMCStatus);
        while (1);
    }

    /* Third stage, increase RPMC counter. */
    /* Input tag is send in to RPMC, it could be time stamp, serial number and so on. */
    /* Here, we use a static date time for demo. */
    u8InputTag[0] = '2';
    u8InputTag[1] = '0';
    u8InputTag[2] = '2';
    u8InputTag[3] = '0';
    u8InputTag[4] = '0';
    u8InputTag[5] = '9';
    u8InputTag[6] = '2';
    u8InputTag[7] = '1';
    u8InputTag[8] = '1';
    u8InputTag[9] = '8';
    u8InputTag[10] = '1';
    u8InputTag[11] = '0';

    u32RPMCStatus = RPMC_IncreaseCounter(DEF_KEY_INDEX, &u8HMACKey[0], &u8InputTag[0]);
    if (u32RPMCStatus == AF_REG_STATUS_SUCCESS)
    {
        // increase counter success
        printf("RPMC_IncreaseCounter success - 0x%02X!!\n", u32RPMCStatus);
    }
    else
    {
        // increase counter fail, check datasheet for the error bit
        printf("RPMC_IncreaseCounter failed - 0x%02X!!\n", u32RPMCStatus);
        while (1);
    }

    /* Counter data in storage in public array counter[], data is available if RPMC_IncreaseCounter() operation succeeded */
    u32RPMCCounter = RPMC_ReadCounter();

    /* Increase RPMC counter done*/
    printf("RPMC_ReadCounter: 0x%X\n", u32RPMCCounter);

    /* Main security operation call challenge*/
    while (1)
    {
        if (RPMC_Challenge(DEF_KEY_INDEX, &u8HMACKey[0], &u8InputTag[0]) != 0)
        {
            printf("RPMC_Challenge failed!!\n");
            /* return signature miss-match */
            break;
        }
        else
        {
            if (count > 500)
            {
                printf("\n");
                /* Increase 32-bit monotonic flash counter. */
                u32RPMCStatus = RPMC_IncreaseCounter(DEF_KEY_INDEX, &u8HMACKey[0], &u8InputTag[0]);
                if (u32RPMCStatus == AF_REG_STATUS_SUCCESS)
                {
                    printf("RPMC_IncreaseCounter success!\n");
                }
                else
                {
                    printf("RPMC_IncreaseCounter failed - 0x%02X!!\n", u32RPMCStatus);
                    break;
                }

                /* Counter data in storage in public array counter[], data is available if RPMC_IncreaseCounter() operation succeeded */
                u32RPMCCounter = RPMC_ReadCounter();
                printf("RPMC_ReadCounter: 0x%X\n", u32RPMCCounter);

                count = 0;
            }
            else
                count++;

            printf(".");

        } //else

    } //while(1)

    printf("Failed");

    return 0;
}
